
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>
#endif

#include "point3d.h"
#define MAXIMUM(a,b) ((a)>(b)?(a):(b))



/* ------------------------------------------------------- */

Point3D::Point3D( )
{
 this->m_data[0] = 0.0;
 this->m_data[1] = 0.0;
 this->m_data[2] = 0.0;
}

/* ------------------------------------------------------- */

Point3D::Point3D( const double x0, const double y0, const double z0 )
{
 this->m_data[0] = x0;
 this->m_data[1] = y0;
 this->m_data[2] = z0;
}

/* ------------------------------------------------------- */

Point3D::Point3D( const Point3D& p )
{
 this->m_data[0] = p.m_data[0];
 this->m_data[1] = p.m_data[1];
 this->m_data[2] = p.m_data[2];
}

/* ------------------------------------------------------- */

Point3D::~Point3D( )
{
}

/* ------------------------------------------------------- */

void Point3D::setX( double x0 )
{
 this->m_data[0] = x0;
}

/* ------------------------------------------------------- */

void Point3D::set( double *d )
{ 
 m_data[0] = d[0];
 m_data[1] = d[1];
 m_data[2] = d[2];
}

/* ------------------------------------------------------- */

void Point3D::get( double *d )
{ 
 d[0] = m_data[0];
 d[1] = m_data[1];
 d[2] = m_data[2];
}

/* ------------------------------------------------------- */

double Point3D::getX( ) const
{ 
 return this->m_data[0];
}

/* ------------------------------------------------------- */

void Point3D::setY( double y0 )
{
 this->m_data[1] = y0;
}

/* ------------------------------------------------------- */

double Point3D::getY( ) const
{ 
 return this->m_data[1];
}

/* ------------------------------------------------------- */

void Point3D::setZ( double z0 )
{
 this->m_data[2] = z0;
}

/* ------------------------------------------------------- */

double Point3D::getZ( ) const
{ 
 return this->m_data[2];
}

/* ------------------------------------------------------- */

void Point3D::setXYZ( const double x0, const double y0, const double z0 )
{
 this->m_data[0] = x0;
 this->m_data[1] = y0;
 this->m_data[2] = z0;
}

/* ------------------------------------------------------- */

void Point3D::setMemory( double *a )
{
 this->m_data[0] = a[0];
 this->m_data[1] = a[1];
 this->m_data[2] = a[2];
}

/* ------------------------------------------------------- */

double* Point3D::getMemory( ) const
{ 
 double *a = (double *)malloc(3 * sizeof(double));

 a[0] = this->m_data[0];
 a[1] = this->m_data[1];
 a[2] = this->m_data[2];

 return a;
}

/* ------------------------------------------------------- */

void Point3D::getMemory( double a[] )
{ 
 a[0] = this->m_data[0];
 a[1] = this->m_data[1];
 a[2] = this->m_data[2];
}

/* ------------------------------------------------------- */

void Point3D::dX( double d )
{
 this->m_data[0] += d;
}

/* ------------------------------------------------------- */

void Point3D::dY( double d )
{
 this->m_data[1] += d;
}

/* ------------------------------------------------------- */

void Point3D::dZ( double d )
{
 this->m_data[2] += d;
}

/* ------------------------------------------------------- */

int Point3D::proximity( const Point3D& p1, double tol ) const
{ 
  Point3D vp (p1 - *this);

  double max = MAXIMUM(fabs(vp.m_data[0]), fabs(vp.m_data[1])); max = MAXIMUM(max, fabs(vp.m_data[2]));

  return(max < tol);
}

/* ------------------------------------------------------- */

int Point3D::isAtSamePosition ( const Point3D& p1, double tol ) const
{ 
  // novo ponto.
  Point3D p2 (p1 - *this);

  // distancia entre os pontos.
  double d = p2.m_data[0] * p2.m_data[0] + 
             p2.m_data[1] * p2.m_data[1] + 
             p2.m_data[2] * p2.m_data[2];

  // check.
  return ( d <= (tol*tol) );
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator = ( const Point3D& p )
{
 this->m_data[0] = p.m_data[0];
 this->m_data[1] = p.m_data[1];
 this->m_data[2] = p.m_data[2];
 return *this;
}

/* ------------------------------------------------------- */

Point3D Point3D::operator - ( ) const
{
 return Point3D ( -this->m_data[0], -this->m_data[1], -this->m_data[2] );
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator += ( const Point3D& p )
{
  this->m_data[0] += p.m_data[0];
  this->m_data[1] += p.m_data[1];
  this->m_data[2] += p.m_data[2];
  return *this;
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator -= ( const Point3D& p )
{
  this->m_data[0] -= p.m_data[0];
  this->m_data[1] -= p.m_data[1];
  this->m_data[2] -= p.m_data[2];
  return *this;
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator *= ( const double sc )
{
  this->m_data[0] *= sc;
  this->m_data[1] *= sc;
  this->m_data[2] *= sc;
  return *this;
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator /= ( const double sc )
{
  this->m_data[0] /= sc;
  this->m_data[1] /= sc;
  this->m_data[2] /= sc;
  return *this;
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator *= ( const Point3D& sc )
{
  this->m_data[0] *= sc.m_data[0];
  this->m_data[1] *= sc.m_data[1];
  this->m_data[2] *= sc.m_data[2];
  return *this;
}

/* ------------------------------------------------------- */

Point3D& Point3D::operator /= ( const Point3D& sc )
{
  this->m_data[0] /= sc.m_data[0];
  this->m_data[1] /= sc.m_data[1];
  this->m_data[2] /= sc.m_data[2];
  return *this;
}

/* ------------------------------------------------------- */

double Point3D::len ( ) const
{
 return sqrt ( this->dotProd ( *this ) );
}

/* ------------------------------------------------------- */

int Point3D::normalize ( Point3D& n ) const
{
 double len = this->len ( );

 if ( len > 0.0 ) { n = *this/len; return 1; }
             
 return 0;
}

/* ------------------------------------------------------- */

int Point3D::normalize ( )
{
 double len = this->len ( );

 if ( len > 0.0 ) 
 { 
   Point3D n = *this/len; 
   this->setXYZ ( n.m_data[0], n.m_data[1], n.m_data[2] );
   return 1; 
 }

 return 0;
}

/* ------------------------------------------------------- */

double Point3D::distance ( const Point3D& p0 ) const
{
 return ( (*this - p0).len ( ) );
}

/* ------------------------------------------------------- */

double Point3D::distance2 ( const Point3D& p0 ) const
{
 return ( (m_data[0] - p0.m_data[0]) * (m_data[0] - p0.m_data[0])+
          (m_data[1] - p0.m_data[1]) * (m_data[1] - p0.m_data[1])+
          (m_data[2] - p0.m_data[2]) * (m_data[2] - p0.m_data[2]) );
}

/* ------------------------------------------------------- */

double Point3D::dotProd ( const Point3D& p0 ) const
{
 return ( this->m_data[0] * p0.m_data[0] + 
          this->m_data[1] * p0.m_data[1] +  
          this->m_data[2] * p0.m_data[2] );
}

/* ------------------------------------------------------- */

Point3D Point3D::crossProd ( const Point3D& p0 ) const
{
  return Point3D ( (this->m_data[1] * p0.m_data[2]) - (this->m_data[2] * p0.m_data[1]),
                     (this->m_data[2] * p0.m_data[0]) - (this->m_data[0] * p0.m_data[2]),
                     (this->m_data[0] * p0.m_data[1]) - (this->m_data[1] * p0.m_data[0]) );
}

/* ------------------------------------------------------- */

double Point3D::tripleProd ( const Point3D& p0, 
                             const Point3D& p1 ) const
{
 return ( this->dotProd ( p0.crossProd ( p1 ) ) );
}

/* ------------------------------------------------------- */

void Point3D::transform ( const double t[4][4] )
{
 double x = this->m_data[0];
 double y = this->m_data[1];
 double z = this->m_data[2];

 this->m_data[0] = t[0][0]*x + t[0][1]*y + t[0][2]*z + t[0][3];
 this->m_data[1] = t[1][0]*x + t[1][1]*y + t[1][2]*z + t[1][3];
 this->m_data[2] = t[2][0]*x + t[2][1]*y + t[2][2]*z + t[2][3];
}

/* ------------------------------------------------------- */

bool
Point3D::colinear( const Point3D &p2, const Point3D& p3, double tol ) const
{
 Point3D u( p2-*this );
 Point3D v( p3-*this );
 Point3D w = u.crossProd( v );
 return ((fabs(w.getX())+fabs(w.getY())+fabs(w.getZ()))<tol);
}

/* ------------------------------------------------------- */

//void Point3D::draw( )
//{
 //GLenum en = glIsEnabled( GL_LIGHTING );
 //if( en == GL_TRUE )
 //    glDisable( GL_LIGHTING );
 //glBegin( GL_POINTS );
 //  glVertex3d( data[0], data[1], data[2] );
 //glEnd( );
 //if( en == GL_TRUE )
 //    glEnable( GL_LIGHTING );
//}

